﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;
using System.Diagnostics;

namespace Xeml.Document.Ontologies.OBO
{
    public class OboStanza : List<Tuple<OboTags, string>>
    {
        public OboStanzaTypes StanzaType { get; set; }
        public OboTermClass HeaderClass { get; set; }
        public List<Xeml.Document.EnvironmentalOntology.VariableContextSpec> contextCollection = new List<EnvironmentalOntology.VariableContextSpec>();
        public Xeml.Document.EnvironmentalOntology.XeoTerm term { get; private set; }


        #region constructors
        protected OboStanza()
            : base()
        {
            HeaderClass = OboTermClass.none;
        }

        public OboStanza(Stream instr)
            : this()
        {
            string line = string.Empty;
            using (TextReader reader = new StreamReader(instr))
            {
                while (reader.Peek() != -1)
                {
                    line = reader.ReadLine();

                    Match headerTagMatch = rxOboStanzaTypes.Match(line);
                    GroupCollection matches = rxOboKeyWords.Match(line).Groups;

                    if (headerTagMatch.Success)
                    {
                        this.StanzaType = (OboStanzaTypes)Enum.Parse(typeof(OboStanzaTypes), headerTagMatch.Value.Replace("[", "").Replace("]", ""), false);
                    }
                    else if (matches["id"].Success)
                    {
                        Add(OboTags.id, matches["id"].Value);
                    }
                    else if (matches["name"].Success)
                    {
                        Add(OboTags.name, matches["name"].Value);
                    }
                    else if (matches["def"].Success)
                    {
                        Add(OboTags.def, matches["def"].Value);
                    }
                    else if (matches["namespace"].Success)
                    {
                        Add(OboTags.@namespace, matches["namespace"].Value);
                    }
                    else if (matches["is_a"].Success)
                    {
                        Add(OboTags.is_a, matches["is_a"].Value.Split('!').First().Trim());
                        //VariableContextSpec ssss = null;
                        //current ist jetzt ein Context
                        //current.ContextCollection.Add(ssss.Name, ssss/*den context der angegeben wurde*/);
                    }
                    else if (matches["instance_of"].Success)
                    {
                        Add(OboTags.instance_of, matches["instance_of"].Value);
                    }
                    else if (matches["has_context"].Success)
                    {
                        Add(OboTags.has_context, matches["has_context"].Value);

                        //res.Attributes.Add(new TermAttribute("Relationship heri als code benutzen", "den Wert benutzen: hasContext XEO:1234"));
                        //res.ContextCollection.Add("hier den Namen vom Context rausssuchen ueber die ID", "das Object über die ID siuchen");
                    }
                    else if (matches["has_enum"].Success)
                    {
                        Add(OboTags.has_enum, matches["has_enum"].Value);
                    }
                    else if (matches["domain"].Success)
                    {
                        Add(OboTags.domain, matches["domain"].Value);
                    }
                    else if (matches["range"].Success)
                    {
                        Add(OboTags.range, matches["range"].Value);
                    }
                    else if (matches["is_metadata_tag"].Success)
                    {
                        Add(OboTags.is_metadata_tag, matches["is_metadata_tag"].Value);
                    }
                    else if (matches["has_symbol"].Success)
                    {
                        Add(OboTags.has_symbol, matches["has_symbol"].Value);
                    }

                    else if (matches["synonym"].Success)
                    {
                        Add(OboTags.synonym, matches["synonym"].Value);
                    }
                    else if (matches["xref"].Success)
                    {
                        Add(OboTags.xref, matches["xref"].Value);
                    }
                    else if (matches["part_of"].Success)
                    {
                        Add(OboTags.part_of, matches["part_of"].Value);
                    }
                    else if (matches["has_datatype"].Success)
                    {
                        Add(OboTags.has_datatype, matches["has_datatype"].Value);
                    }
                    else if (matches["comment"].Success)
                    {
                        Add(OboTags.comment, matches["comment"].Value);
                    }
                    else if (matches["relationship"].Success)
                    {
                        Add(OboTags.relationship, matches["relationship"].Value);
                    }
                    else if (matches["is_obsolete"].Success)
                    {
                        Add(OboTags.is_obsolete, matches["is_obsolete"].Value);
                    }
                    else if (matches["intersection_of"].Success)
                    {
                        Add(OboTags.intersection_of, matches["intersection_of"].Value);
                    }
                    else if (matches["alt_id"].Success)
                    {
                        Add(OboTags.alt_id, matches["alt_id"].Value);
                    }
                    else if (matches["disjoint_from"].Success)
                    {
                        Add(OboTags.disjoint_from, matches["disjoint_from"].Value);
                    }
                    else if (matches["consider"].Success)
                    {
                        Add(OboTags.consider, matches["consider"].Value);
                    }
                    else if (matches["replaced_by"].Success)
                    {
                        Add(OboTags.replaced_by, matches["replaced_by"].Value);
                    }
                    else if (matches["created_by"].Success)
                    {
                        Add(OboTags.created_by, matches["created_by"].Value);
                    }
                    else if (matches["creation_date"].Success)
                    {
                        Add(OboTags.creation_date, matches["creation_date"].Value);
                    }
                    else if (matches["is_transitive"].Success)
                    {
                        Add(OboTags.is_transitive, matches["is_transitive"].Value);
                    }
                    else if (matches["subset"].Success)
                    {
                        Add(OboTags.subset, matches["subset"].Value);
                    }

                    else if (matches["xref_analog"].Success)
                    {
                        Add(OboTags.xref_analog, matches["xref_analog"].Value);
                    }
                    else
                    {
                        Add(OboTags.XXX_unknown, line);
                    }
                }
            }
        }

        public OboStanza(OboStanza store)
            : base(store)
        {
            this.StanzaType = store.StanzaType;
            this.HeaderClass = store.HeaderClass;
        }
        #endregion constructors

        public new void Clear()
        {
            base.Clear();
            base.TrimExcess();
            contextCollection.Clear();
            contextCollection.TrimExcess();
            term = null;
        }

        public Xeml.Document.EnvironmentalOntology.XeoTerm GetTerm(string NameSpace, string _nameSpaceAlias)
        {
            term = new Xeml.Document.EnvironmentalOntology.XeoTerm(Id, NameSpace, _nameSpaceAlias);
            return term;
        }

        public string Id
        {
            get
            {
                System.Diagnostics.Debug.Assert(this.Count(x => x.Item1 == OboTags.id) == 1);
                return this.Single(x => x.Item1 == OboTags.id).Item2;
            }
        }
        public string Name
        {
            get
            {
                System.Diagnostics.Debug.Assert(this.Count(x => x.Item1 == OboTags.name) == 1);
                return this.Single(x => x.Item1 == OboTags.name).Item2;
            }
        }

        public bool isEnvironmentVariableTerm { get { return HeaderClass == OboTermClass.EnvironmentVariable; } }
        public bool isContextTerm { get { return HeaderClass == OboTermClass.Context; } }
        public bool isDataTypeTerm { get { return HeaderClass == OboTermClass.DataType; } }

        public bool isHeaderTypeTerm { get { return StanzaType == OboStanzaTypes.Term; } }

        public bool isNoneTerm { get { return HeaderClass == OboTermClass.none; } }
        
        //some genreral Tag information from the obo file
        public bool isObsolete { get { return this.Any(x => x.Item1 == OboTags.is_obsolete); } }

        private void Add(OboTags key, string value)
        {
            base.Add(new Tuple<OboTags, string>(key, value));
        }

        public bool Any(OboTags what)
        {
            return this.Any(x => x.Item1 == what);
        }

        #region Regular Expressions
        public static Regex rxOboStanzaTypes = new Regex(@"^\[Term\]|\[Instance\]|\[Typedef\]$", System.Text.RegularExpressions.RegexOptions.Compiled | System.Text.RegularExpressions.RegexOptions.Singleline);

        private static Regex rxOboKeyWords = new Regex(
            //@"^format-version:\s*(?<format_version>.+)$|" + //6
            //@"^date:\s*(?<date>.+)$|" + //7
            //@"^saved-by:\s*(?<saved_by>.+)$|" + //8
            //@"^auto-generated-by:\s*(?<auto_generated_by>.+)$|" + //9
            //@"^id_space:\s*(?<id_space>.+)$|" + //10

            @"^id:\s*(?<id>.+)$|" +
            @"^name:\s*(?<name>.+)$|" +
            @"^def:\s*(?<def>.+)$|" +
            @"^namespace:\s*(?<namespace>.+)$|" +
            @"^instance_of:\s*(?<instance_of>.+)$|" +
            @"^is_a:\s*(?<is_a>.+)$|" +
            @"^relationship: has_context\s*(?<has_context>.+)$|" +
            @"^relationship: has_enum\s*(?<has_enum>.+)$|" +
            @"^relationship: part_of\s*(?<part_of>.+)$|" +
            @"^relationship: has_datatype\s*(?<has_datatype>.+)$|" +
            @"^domain:\s*(?<domain>.+)$|" +
            @"^range:\s*(?<range>.+)$|" +
            @"^is_metadata_tag:\s*(?<is_metadata_tag>.+)$|" +
            @"^property_value: has_symbol\s*(?<has_symbol>.+)$|" +

            //additionally comming from EnvO
            @"^synonym:\s*(?<synonym>.+)$|" +
            @"^xref:\s*(?<xref>.+)$|" +
            @"^comment:\s*(?<comment>.+)$|" +
            @"^relationship:\s*(?<relationship>.+)$|" +
            @"^is_obsolete:\s*(?<is_obsolete>.+)$|" +
            @"^intersection_of:\s*(?<intersection_of>.+)$|" +
            @"^subset:\s*(?<subset>.+)$|" +
            @"^alt_id:\s*(?<alt_id>.+)$|" +
            @"^disjoint_from:\s*(?<disjoint_from>.+)$|" +
            @"^consider:\s*(?<consider>.+)$|" +
            @"^replaced_by:\s*(?<replaced_by>.+)$|" +
            @"^created_by:\s*(?<created_by>.+)$|" +
            @"^creation_date:\s*(?<creation_date>.+)$|" +
            @"^is_transitive:\s*(?<is_transitive>.+)$|" +

            //additionally comming from EO
            @"^xref_analog:\s*(?<xref_analog>.+)$"

            , RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture | RegexOptions.Compiled | RegexOptions.Singleline);
        #endregion Regular Expressions
    }
}
